package core.proto;

import static app.constants.CollabVizConstants.CURRENT_DOC_NOTE_COLOR;
import static app.constants.CollabVizConstants.DEFAULT_NOTE_COLOR;
import static app.constants.CollabVizConstants.DUMMY_FILE_NAME;
import static app.constants.CollabVizConstants.SEARCH_NOTE_COLOR;
import static app.constants.CollabVizConstants.SELECTED_ROW_COLOR;

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Rectangle;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.File;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.Set;

import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.DefaultCellEditor;
import javax.swing.DropMode;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.SortOrder;
import javax.swing.TransferHandler;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.TableColumn;
import javax.swing.table.TableColumnModel;
import javax.swing.table.TableModel;
import javax.swing.table.TableRowSorter;

import org.apache.commons.lang.StringUtils;

import core.proto.search.SearchBar;

import app.exceptions.CollabVizException;
import app.service.xml.XMLService;
import app.xml.model.UserNote;
import app.xml.model.UserNoteCollection;

/**
 * Class for the entire notes area. 
 */
public class NotesArea {
	JTable table;
	JPanel historyArea; 
	public static final int[] COLUMN_WIDTHS = {20, 150, 150, 80, 100};

	/**
	 * Reads in the notes from disk and initializes the notes table. 
	 */
	public NotesArea() {
		initializeTable();
	}

	/**
	 * To update the row height and multiple rows in it
	 */
	public void updateRowHeights()
	{
	    try
	    {
	        for (int row = 0; row < table.getRowCount(); row++)
	        {
	            int rowHeight = table.getRowHeight();

	            for (int column = 0; column < table.getColumnCount(); column++)
	            {
	                Component comp = table.prepareRenderer(table.getCellRenderer(row, column), row, column);
	                rowHeight = Math.max(rowHeight, comp.getPreferredSize().height);
	            }

	            table.setRowHeight(row, rowHeight);
	        }
	    }
	    catch(ClassCastException e) {}
	}
	/**
	 * For initialization purposes
	 */
	
	private void initializeTable() {
		try {			
			UserNoteCollection userNotes = (UserNoteCollection) XMLService.unmarshall(Util.getXmlOutputFilePath());
			// There are no existing notes. 
			if (userNotes == null) {
				userNotes = new UserNoteCollection();
			}
			NoteTableModel noteTableModel = new NoteTableModel(userNotes);
			noteTableModel.addTableModelListener(new NoteTableModelListener());
			table = new JTable(noteTableModel);
			setColumnWidths();

			// for the drop effect
			HisTransferHandler dnd = new HisTransferHandler();
			table.setDropMode(DropMode.ON);
			table.setTransferHandler(dnd);
			table.setDragEnabled(true);

			// for sorting. sorter basically sorts by string or 
			// calls the compareTo method if the data object implements
			// Comparable. see the JTable tutorial for complete info. 
			TableRowSorter<TableModel> sorter = new TableRowSorter<TableModel>(noteTableModel);
			table.setAutoCreateColumnsFromModel(false);
			table.setRowSorter(sorter);
			setupComparators(sorter);

			table.setDefaultEditor(UserNote.class, new NoteTableCellEditor(new JTextField()));

			ColorRenderer colorRenderer = new ColorRenderer();
			table.setDefaultRenderer(UserNote.class, colorRenderer);
			table.setDefaultRenderer(Integer.class, colorRenderer);

			table.addMouseListener(new ClickHandler());
			table.addKeyListener(new DeleteKeyHandler());
			table.addKeyListener(new EnterKeyHandler());
			// Finally don't let people rearrange the columns in the table. 
			table.getTableHeader().setReorderingAllowed(false);
		} catch (CollabVizException cve) {
			cve.printStackTrace();
			throw new Error(cve);
		}
	}

	/**
	 * Scroll to the last edited note corresponding to the 
	 * document with the given filepath. Recall that we are 
	 * using the filePath as the unique key for documents.
	 * @param filePath 
	 * @param ignoreFilePath whether to ignore the file path arg. 
	 */
	public void scrollToLatestNoteEntry(String filePath, boolean ignoreFilePath) {
		UserNote userNote = null;

		int rowCount = 0;
		int row2ScrollTo = -1;
		for(UserNote n : ((NoteTableModel) table.getModel()).getUserNotes()){
			if (ignoreFilePath || n.getFilePath().equals(filePath)){
				if (userNote == null || n.getTimeStamp().compareTo(userNote.getTimeStamp()) >= 0){
					userNote = n;
					row2ScrollTo = rowCount;
				}
			}
			++rowCount;
		}
		// We need to do the scrolling if there is a note. 
		if (userNote != null){
			// Special case: this happens at start of system. Scroll to the blank row if possible. 
			if (row2ScrollTo + 2 == table.getRowCount()){
				row2ScrollTo += 1;
			}
			Rectangle cellRect = table.getCellRect(table.convertRowIndexToView(row2ScrollTo), 0, true);
			// sometimes the JTable's scrolling doesn't scroll the last row entirely,
			// so this is a little hack to make the last edited row entirely visible. 
			if (cellRect.y < 400){
				cellRect.y -= 3 * cellRect.height;
			}else{
				cellRect.y += 3 * cellRect.height;
			}
			historyArea.scrollRectToVisible(cellRect);
			table.invalidate();
		}
	}

	private void setupComparators(TableRowSorter<TableModel> sorter) {
		for(int i=0; i<table.getModel().getColumnCount(); ++i){
			sorter.setComparator(i, new UserNoteComparator(i, sorter));
		}
	}

	private class NoteTableCellEditor extends DefaultCellEditor {

		private static final long serialVersionUID = 1L;

		JTextField textField;


		public NoteTableCellEditor(JTextField textField) {
			super(textField);
			this.textField = textField;
		}

		public Object getCellEditorValue() {
			return textField.getText();
		}

		public Component getTableCellEditorComponent(JTable jTable,
				Object value,
				boolean isSelected,
				int row,
				int column) {
			// set the text of the text field to the value of the corresponding field in the user note. 
			textField.setText(((NoteTableModel) table.getModel()).getStringValueAt((UserNote) value, column, row));
			// the text field is used to do the editing. 
			return textField;
		}
	}

	/**
	 * This takes care of always putting the blank row at the end of the table when doing column sorts. 
	 */
	private class UserNoteComparator implements Comparator<UserNote> {

		static final int GREATER_THAN = 1;
		static final int LESSER_THAN = -1;

		int column;
		TableRowSorter<TableModel> sorter;

		public UserNoteComparator (int column, TableRowSorter<TableModel> sorter) {
			this.column = column;
			this.sorter = sorter;
		}

		/**
		 * If one of the notes is a blank row, it's always the greatest. 
		 * Else just do a string comparison. 
		 * @return 1 if note 1 greater than note2, -1 if less, 0 if equal. 
		 */
		@Override
		public int compare(UserNote note1, UserNote note2) {

			// There should only be 1 sort key because we always sort by 1 column. 
			if (note1.isBlankRow()){
				return sorter.getSortKeys().get(0).getSortOrder() == SortOrder.ASCENDING ? LESSER_THAN : GREATER_THAN;
			}else if (note2.isBlankRow()){
				return sorter.getSortKeys().get(0).getSortOrder() == SortOrder.ASCENDING ? LESSER_THAN : GREATER_THAN;
			}

			// Special sorting using Date
			if (column == NoteTableModel.TIMESTAMP_COL){
				return note1.getTimeStamp().compareTo(note2.getTimeStamp());
			}else{
				return ((NoteTableModel) table.getModel()).getStringValueAt(note1, column, 0).compareTo(((NoteTableModel) table.getModel()).getStringValueAt(note2, column, 0));
			}
		}
	}

	private void setColumnWidths() {
		TableColumnModel columnModel = table.getTableHeader().getColumnModel();

		for(int i=0; i < COLUMN_WIDTHS.length; ++i){
			columnModel.getColumn(i).setPreferredWidth(COLUMN_WIDTHS[i]);
		}
	}

	//set the length of the columns back to default after pressing "enter" key because it was changed 
	//previously in mouse listener so that user can see that user entered data can be as long as they want
	private class EnterKeyHandler extends KeyAdapter {
		public void keyReleased (KeyEvent e){
			if (e.getKeyCode() == KeyEvent.VK_ENTER){
				setColumnWidths();
			}
		}
		}
	private class DeleteKeyHandler extends KeyAdapter {

		/**
		 * This handles deletion of selected rows via pressing of delete key. 
		 */
		@Override
		public void keyReleased (KeyEvent e){
			if (e.getKeyCode() == KeyEvent.VK_DELETE){
				// If we are editing a cell ignore the delete key press. 
				if (! table.isEditing()){
					int[] rowsAscending = table.getSelectedRows();
					for(int i=0; i<rowsAscending.length; ++i){
						rowsAscending[i] = table.convertRowIndexToModel(rowsAscending[i]);
					}
					Arrays.sort(rowsAscending);
					if (((NoteTableModel) table.getModel()).deleteRows(rowsAscending) > 0){
						((NoteTableModel) table.getModel()).dumpToDisk();
					}
				}
			}
		}
	}

	private class ClickHandler extends MouseAdapter {
		public void mouseClicked(MouseEvent e) {
			TableColumn col = table.getColumnModel().getColumn(table.getSelectedColumn());
			if (e.getClickCount() == 1) {
				if (table.getSelectedColumn() == NoteTableModel.USER_NOTE_COL){
					//Sets the length of the user entered data as longer when clicked to give the user that data field is not restricted
					col.setPreferredWidth(500);
					table.editCellAt(table.getSelectedRow(), table.getSelectedColumn());
				}
				if (table.getSelectedColumn() == NoteTableModel.ANNOTATION_COL){
					//Sets the length of the user copied data as longer when clicked to give the user that copied field is not restricted
					col.setPreferredWidth(500);
				}
			} else if (e.getClickCount() == 2) {
				int row = table.convertRowIndexToModel(table.getSelectedRow());
				String filePath = ((NoteTableModel) table.getModel()).getFilePathOf(row);

				// This happens when user entered a note but had no document open at the time. 
				if (! filePath.equals(DUMMY_FILE_NAME)){
					AllComponents.getFileTree().expandTreeByPath(filePath, true);
				}
			}
		}
	}

	private static class NoteTableModel extends AbstractTableModel {
		private static final long serialVersionUID = 1L;

		// Columns for the notes table. 
		public static final int ROW_COL = 0;
		public static final int USER_NOTE_COL = 1;
		public static final int ANNOTATION_COL = 2;
		public static final int FILE_NAME_COL = 3;
		public static final int TIMESTAMP_COL = 4;
		public static final String[] COLUMN_NAMES = {"Row", "User entered data", "Data from document", "File name", "Timestamp" };
		public static final Class[] COLUMN_CLASSES = {Integer.class, UserNote.class, UserNote.class, UserNote.class, UserNote.class};

		UserNoteCollection userNotes;
		Comparator<UserNote> comparatorByTimestamp; // This is to sort the data in the 

		/**
		 * Appends blank row if there is no existing notes. 
		 * Otherwise there would have been a blank row at the 
		 * end of table that was serialized along with the rest
		 * of the notes, in which case we don't need to 
		 * append a blank row. Also sorts user notes by timestamp. 
		 * @param userNotes 
		 */
		public NoteTableModel(UserNoteCollection userNotes) { 
			this.userNotes = userNotes;
			if (userNotes.size() == 0){
				appendBlankRow();
			}
			comparatorByTimestamp = new Comparator<UserNote> (){
				// Sort the two things by name. Make the blank row go to bottom as usual.
				public int compare(UserNote note1, UserNote note2) {
					if (note1.isBlankRow()){
						return 1;
					}else if (note2.isBlankRow()){
						return -1;
					}else{
						return note1.getTimeStamp().compareTo(note2.getTimeStamp());
					}
				}
			};
			Collections.sort(userNotes.getNoteList(), comparatorByTimestamp);
			fireTableDataChanged();
		}		

		public String getFilePathOf(int row) {
			return isLegalLocation(row, 0) ? userNotes.getNoteList().get(row).getFilePath() : "";
		}

		public void dumpToDisk () {
			try {
				XMLService.marshall(Util.getXmlOutputFilePath(), userNotes);
			} catch (CollabVizException e) {
			}
		}

		/**
		 * Row is in model coordinates. 
		 * @param row
		 * @return
		 */
		public UserNote getNoteAtRow (int row) {
			if (isLegalLocation(row, 0)){
				return userNotes.getNoteList().get(row);
			}
			return null;
		}

		/**
		 * User can only edit the user note column.  
		 */
		public boolean isCellEditable(int row, int column) {
			return column == USER_NOTE_COL;
		}

		public void addRow (UserNote userNote){
			userNotes.add(userNote);
			fireTableDataChanged();
		}

		public void appendBlankRow () {
			addRow(new UserNote());
		}

		/**
		 * Precondition: given rows are valid, and sorted in ascending order.   
		 * Delete the given user note only if it is not the blank row.
		 * @param row The row to delete.
		 * @return The number of rows deleted.  
		 */
		public int deleteRows (int[] rowsAscending) {
			int numRowsDeleted = 0;
			boolean dataChanged = false;

			for(int i=rowsAscending.length-1; i>=0; --i){
				UserNote note = userNotes.getNoteList().get(rowsAscending[i]);
				if (! note.isBlankRow()){
					++numRowsDeleted;
					userNotes.getNoteList().remove(rowsAscending[i]);
					dataChanged = true;
				}
			}

			if (dataChanged){
				fireTableDataChanged();
			}

			return numRowsDeleted;
		}

		@Override
		public Class getColumnClass(int column) {
			if (column >= 0 && column < COLUMN_CLASSES.length){
				return COLUMN_CLASSES[column];
			}
			return Object.class;
		}

		@Override
		public String getColumnName(int column) {
			return COLUMN_NAMES[column];
		}

		@Override
		public int getColumnCount() {
			return COLUMN_NAMES.length;
		}

		@Override
		public int getRowCount() {
			return userNotes.size();
		}

		private boolean isLegalLocation(int rowIndex, int columnIndex) {
			return ((rowIndex >= 0 && rowIndex < userNotes.size()) && 
					(columnIndex >= 0 && columnIndex < COLUMN_CLASSES.length));
		}

		public String getStringValueAt (UserNote note, int column, int row) {
			// This is a hard coded mapping of columns to the fields in the data class. 
			switch (column) {
			case USER_NOTE_COL:
				return note.getNote();
			case ANNOTATION_COL:
				return note.getAnnotation();
			case FILE_NAME_COL:
				return note.getFileName();
			case TIMESTAMP_COL:
				return note.getTimeStamp().toString();
			case ROW_COL:
				// The row value plus 1, since row is 0-indexed. 
				return String.valueOf(row + 1);
			default:
				throw new Error("Not supported");
			}
		}

		@Override
		public Object getValueAt(int rowIndex, int columnIndex) {
			if (isLegalLocation(rowIndex, columnIndex)){
				return userNotes.getNoteList().get(rowIndex);
			}
			return null;
		}

		/**
		 * @return DUMMY_FILE_NAME if no current doc is open. 
		 */
		private String getCurrentDocName () {
			String path = getCurrentDocFilePath();
			return path.equals(DUMMY_FILE_NAME) ? DUMMY_FILE_NAME : new File(path).getName();
		}

		/**
		 * Asks AllComponents what the current HTML file is.
		 * @return DUMMY_FILE_NAME if path invalid. 
		 */
		private String getCurrentDocFilePath () {
			String path = AllComponents.getHtmlFilePath();
			return path == null || path.trim().equals("") ? DUMMY_FILE_NAME : path;
		}

		public Iterable<UserNote> getUserNotes (){
			return userNotes.getNoteList();
		}

		public void setValueAt(Object value, int rowIndex, int columnIndex) {
			if (isLegalLocation(rowIndex, columnIndex)) {
				UserNote row = userNotes.getNoteList().get(rowIndex);
				boolean valueChanged = false; 

				// This is a hard coded mapping of columns to the fields in the data class. 
				switch (columnIndex) {
				case USER_NOTE_COL:
					// Remove blank spaces around user input before comparing. 
					// This helps prevent adding a new row when user just entered
					// a space into a blank row. 
					valueChanged = ! ((String) value).trim().equals(row.getNote());
					if (valueChanged){
						if (row.isBlankRow()){
							appendBlankRow();
						}
						// We change the note and time stamp. 
						row.setNote((String) value);
						row.setTimeStamp(new Date());
						row.setFileName(getCurrentDocName());
						row.setFilePath(getCurrentDocFilePath());								
					}
					break;
				case ANNOTATION_COL:
					valueChanged = ! ((String) value).trim().equals(row.getAnnotation());
					if (valueChanged){
						if (row.isBlankRow()){
							appendBlankRow();
						}
						row.setAnnotation((String) value);
						row.setTimeStamp(new Date());
						row.setFileName(getCurrentDocName());
						row.setFilePath(getCurrentDocFilePath());
					}
					break;
				default:
					// User cannot set the value of the file name or the time stamp! 
					break; 
				}
				if (valueChanged){
					// Sort the data by timestamp. 
					Collections.sort(userNotes.getNoteList(), comparatorByTimestamp);
					fireTableDataChanged();
				}
			}
		}
	}

	/**
	 * This class listens for any data changes and dumps the data to disk when it happens. 
	 */
	private class NoteTableModelListener implements TableModelListener {
		@Override
		public void tableChanged(TableModelEvent tme) {
			// Whenever we update, we dump all data back to disk. 
			if (tme.getType() == TableModelEvent.UPDATE && tme.getFirstRow() != TableModelEvent.HEADER_ROW) {
				((NoteTableModel) table.getModel()).dumpToDisk();
			}
		}
	}

	/**
	 * Sets the search string that is used to determine 
	 * whether to highlight a table cell (iff its content
	 * partially matches the search string). 
	 * @param searchString
	 */
	public void setSearchString (String searchString) {
		((ColorRenderer) table.getDefaultRenderer(UserNote.class)).setSearchString(searchString); 
	}

	/**
	 * Class used to render highlights in the table of notes. 
	 * This only works with things of String type because 
	 * DefaultTableCellRenderer is a JLabel. 
	 */
	private class ColorRenderer extends DefaultTableCellRenderer {

		final long serialVersionUID = 1L;
		static final String INVALID_SEARCH_STRING = "";
		String searchString = INVALID_SEARCH_STRING;

		public ColorRenderer () { 
			super();
			setOpaque(true); // MUST do this for background to show up
		}

		public void setSearchString (String searchString){
			this.searchString = searchString;
		}

		/*
		 * Returns the JComponent that is used to render an object 
		 * of this type. Currently supports Strings and Booleans. 
		 */
		@Override 
		public Component getTableCellRendererComponent(JTable table,
				Object value, boolean isSelected, boolean hasFocus, int row,
				int column) {

			String cellContent = ((NoteTableModel) table.getModel()).getStringValueAt((UserNote) value, column, row);

			if (! ((UserNote) value).isBlankRow())
				setText(cellContent);
			else
				setText(""); // Make sure blank row is blank. 

			setBackground(DEFAULT_NOTE_COLOR);
			// Check if note corresponds to current doc, if so highlight. 
			String noteFilePath = ((NoteTableModel) table.getModel()).getNoteAtRow(table.convertRowIndexToModel(row)).getFilePath();
			// No idea whether this returns null, so just to be safe we will always check. 
			String currentDocPath = AllComponents.getCurrentPath();
			if (currentDocPath != null && ! noteFilePath.equals(DUMMY_FILE_NAME)){
				if (noteFilePath.equals(currentDocPath)){
					setBackground(CURRENT_DOC_NOTE_COLOR);
					String annotation = ((NoteTableModel) table.getModel()).getNoteAtRow(table.convertRowIndexToModel(row)).getAnnotation();
					System.out.println("annotation: "+ annotation);

					if(! annotation.equals(""))
					{
						PaintDoc doc = AllComponents.getUpdatedDocument();
						AllComponents.highlightDoc(doc, annotation);
					}
					/* Please remove later when cleaning up code. 
					 * PaintDoc currentDoc = (PaintDoc)AllComponents.getDocumentPane();
					Document currentDocument= currentDoc.getDocument();
					String currentDocumentText = "";
					try {
						currentDocumentText = currentDocument.getText(0, currentDocument.getLength());
					} catch (BadLocationException e) {
						e.printStackTrace();
					}
					System.out.println("currentDocumentText: " + currentDocumentText + "\nlength: " + currentDocumentText.length());
					String annotation = ((NoteTableModel) table.getModel()).getNoteAtRow(table.convertRowIndexToModel(row)).getAnnotation();
					if(!annotation.equals(""))
					{
							int annotationIndex = ((NoteTableModel) table.getModel()).getNoteAtRow(table.convertRowIndexToModel(row)).getAnnotationIndex();
							AllComponents.getDocumentPane().setText(TextHighlighter.highlightAnnotations(annotation,annotationIndex,currentDocumentText));
					}*/
				}
			}
			// Check if note is a partial match. Do a case-insensitive search.
			if (! searchString.equals(INVALID_SEARCH_STRING) && cellContent.toLowerCase().indexOf(searchString) != -1){
				setBackground(SEARCH_NOTE_COLOR);
			}
			// Finally, if row is selected, make it blue. This (obviously)
			// overrides all the other coloring. 
			if (isSelected){
				setBackground(SELECTED_ROW_COLOR);
			}

			setToolTipText(cellContent);

			return this;
		}
	}

	public JTable getTable() {
		return table;
	}

	/**
	 * TODO decide whether to keep this. 
	 * This method will highlight the set of text in the history content. *
	 */
	public Rectangle highlightTextSet(Set<String> cellValues) {
		if (cellValues == null || cellValues.isEmpty()) {
			return null;
		}
		Rectangle rect = null;
		//cellValueSet = cellValues;

		// colorRender.setBackground(Color.white);
		boolean isRectSet = false;
		for (String str : cellValues) {
			/* TODO
			String cellParam[] = cellValueMap.get(str).split(":");
			table.setValueAt(str, Integer.parseInt(cellParam[0]), Integer
					.parseInt(cellParam[1]));
			if (!isRectSet) {
				rect = table.getCellRect(Integer.parseInt(cellParam[0]),
						Integer.parseInt(cellParam[1]), true);
			}*/
		}
		table.updateUI();
		return rect;
	}

	/**
	 * This method will add the history component to the screen.
	 */
	public NotesArea addHistoryComponent(JScrollPane historyScroll,
			SearchBar searchBar) {
		historyArea = new JPanel();
		historyArea.setLayout(new BorderLayout(10, 20));

		Box boxes[] = new Box[2];
		boxes[0] = Box.createVerticalBox();
		boxes[1] = Box.createHorizontalBox();
		boxes[0].add(getTable().getTableHeader());
		boxes[0].add(getTable());

		// search bar
		boxes[1].add( Box.createHorizontalGlue());
		boxes[1].add(searchBar);
		//boxes[1].add(Box.createRigidArea(new Dimension(350, 5)));
		historyScroll.setViewportView(historyArea);
		historyArea.add(boxes[0], BorderLayout.NORTH);
		historyArea.add(boxes[1], BorderLayout.EAST);
		return this;
	}

	/**
	 * Sets the history area as viewport.
	 */
	public void setViewportViewForHistory(JScrollPane historyScroll) {
		historyScroll.setViewportView(historyArea);
	}

	/**
	 * Takes of drop action. 
	 */
	private class HisTransferHandler extends TransferHandler {
		private static final long serialVersionUID = 1L;

		public boolean canImport(TransferHandler.TransferSupport support) {
			if (!support.isDataFlavorSupported(DataFlavor.stringFlavor)) {
				return false;
			}
			JTable.DropLocation dl = (JTable.DropLocation) support.getDropLocation();
			try {
				String data = (String) support.getTransferable().getTransferData(DataFlavor.stringFlavor);
				if (data != null && !StringUtils.isEmpty(data)) {
					return true;
				}
			} catch (Exception e) {
			}
			return false;
		}

		public boolean importData(TransferHandler.TransferSupport support) {
			if (!canImport(support)) {
				return false;
			}
			Transferable transferable = support.getTransferable();
			String data;
			try {
				data = (String) transferable
				.getTransferData(DataFlavor.stringFlavor);
				JTable.DropLocation dl = (JTable.DropLocation) support.getDropLocation();
				int column = table.convertColumnIndexToModel(dl.getColumn());
				int row = table.convertRowIndexToModel(dl.getRow());
				if (data != null & !StringUtils.isEmpty(data)) {
					// set the dragged text in the corresponding column, row...
					table.getModel().setValueAt(data, row, column);
					UserNote note = ((NoteTableModel) table.getModel()).getNoteAtRow(row);
					int dot = AllComponents.getDocumentPane().getCaret().getDot();
					int mark = AllComponents.getDocumentPane().getCaret().getMark();
					if(dot < mark)
					{
						note.setAnnotationIndex(new Integer(dot));
					}
					else
					{
						note.setAnnotationIndex(new Integer(mark));
					}
					System.out.println("dot: " + dot);
					System.out.println("mark: " + mark + "\n\n\n\n");
					System.out.println(AllComponents.getDocumentPane().getSelectionStart());
					((NoteTableModel) table.getModel()).dumpToDisk();
					AllComponents.getDocumentPane().repaint();
					// this makes sure that it will get the index of the front of the selection regardless of if you
					// highlight it backwards or forwards

				}
			} catch (Exception e) {
				return false;
			}
			return false;
		}
	}
}